package ca.usask.cs.decisiontree.XMLHandler;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

//For jdk1.5 with built in xerces parser
//import com.sun.org.apache.xml.internal.serialize.OutputFormat;
//import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

//For JDK 1.3 or JDK 1.4  with xerces 2.7.1
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xml.serialize.OutputFormat;
/**
 * 
 * @editor Wenyi An
 *
 */

@SuppressWarnings("deprecation")
public class WriteXML {

	private Document dom;

	public WriteXML() {

		//Get a DOM object
		createDocument();
		//createDOMTree();
	}

	/**
	 * Using JAXP in implementation independent manner create a document object
	 * using which we create a xml tree in memory
	 */
	private void createDocument() {

		//get an instance of factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
		//get an instance of builder
		DocumentBuilder db = dbf.newDocumentBuilder();

		//create an instance of DOM
		dom = db.newDocument();

		}catch(ParserConfigurationException pce) {
			//dump it
			JOptionPane.showMessageDialog(null,"Error while trying to instantiate DocumentBuilder " + pce);
	//		System.out.println("Error while trying to instantiate DocumentBuilder " + pce);
			System.exit(1);
		}

	}

    /**
     * creates the root node of the element and add to dom
     * @return the rootNode Element
     */
    public Element setup()
    {
        //create the root element <Books>
		Element rootEle = dom.createElement("DecisionTree");
		dom.appendChild(rootEle);

        return rootEle;
    }

    /**
     * Creates a Basic DecisionTree Element and append it to the curEle
     * @param curEle the current element in which the BDTree Element will append to
     * @param theTreeName treeName of the BasicDecisionTree
     * @param theTreeID treeID of the BasicDecisionTree
     * @return
     */
    public Element CreateBasicDecisionTree(Element curEle, String theTreeName, int theTreeID)
    {
    	Element BDTree = createBDTreeEle(theTreeName,theTreeID);
        //Append the BDTree to the curEle
        curEle.appendChild(BDTree);   
		return BDTree;
    }
    public Element createBDTreeEle(String theTreeName,int theTreeID){
    	Element BDTree = dom.createElement("DecisionTree");
 		BDTree.setAttribute("type", "BasicDecisionTree");
 		//create TreeName element with its appropriate value and append to BDTree
 		String newTreeID = ""+theTreeID;
 		createChildren(BDTree,new String[]{"treeName","treeID"},new String[] {theTreeName,newTreeID});
 	
 		return BDTree;
    }

    /**
     * Creates BasicEdge element with given info and append to curEle
     * @param curEle element in which the bEdge appends to
     * @param theEdgeID edgeID of the BasicEdge
     * @param theVal value of the BasicEdge
     * @param theSelected selected of the BasicEdge
     * @param theSourceID sourceID of the BasicEdge
     * @param theDestID destID of the BasicEdge
     * @return the bEdge element
     */
    public Element CreateBasicEdgeElement(Element curEle, int theEdgeID, String theEdgeName, double theVal, boolean theSelected, int theSourceID, int theDestID)
    {     
        Element bEdge = createbEdgeEle(theEdgeID,theEdgeName,theVal,theSelected,theSourceID,theDestID);
        //append bEdge to the current element
        curEle.appendChild(bEdge);

		return bEdge;
    }
   
    
    public Element createbEdgeEle(int theEdgeID, String theEdgeName, double theVal, boolean theSelected, int theSourceID, int theDestID){
    	 Element bEdge = dom.createElement("Edge");
 		bEdge.setAttribute("type", "BasicEdge");

 		
 		//create edgeID element with its values and append to bEdge Element
 		String newEdgeID = ""+theEdgeID;
 		String newEdgeName = ""+theEdgeName;
 		String newVal = ""+theVal;
 		String newSelected = ""+theSelected;
 		String newSourceID = ""+ theSourceID;
 		String newDestID = ""+ theDestID;
 		
 		createChildren(bEdge, new String [] {"edgeID","edgeName","value","selected","sourceNodeID","destinationNodeID"},new String []{newEdgeID,newEdgeName,newVal,newSelected,newSourceID,newDestID});
 	
 		return bEdge;
  
    }
   

    /**
     * Creates ChanceEdge element with given info and append to curEle
     * @param curEle element in which the cEdge appends to
     * @param theEdgeID edgeID of the ChanceEdge
     * @param theVal value of the ChanceEdge
     * @param theSelected selected of the ChanceEdge
     * @param theSourceID sourceID of the ChanceEdge
     * @param theDestID destID of the ChanceEdge
     * @return the cEdge element
     */
    public Element CreateChanceEdgeElement(Element curEle, int theEdgeID,String theEdgeName, double theVal, double theProb, boolean theSelected, int theSourceID, int theDestID)
    {
        Element cEdge = createcEdgeEle(theEdgeID,theEdgeName, theVal, theProb, theSelected, theSourceID, theDestID);
        //append DNode to the current element
        curEle.appendChild(cEdge);

		return cEdge;
    }
    public Element createcEdgeEle(int theEdgeID,String theEdgeName, double theVal, double theProb, boolean theSelected, int theSourceID, int theDestID){
    	 Element cEdge = dom.createElement("Edge");
 		cEdge.setAttribute("type", "ChanceEdge");

 		//create edgeID element with its values and append to DNode Element
 		String newEdgeID = ""+theEdgeID;
 		String newEdgeName = theEdgeName;
 		String newVal = ""+theVal;
 		String newProb = ""+theProb;
 		String newSelected = ""+theSelected;
 		String newSourceID = ""+ theSourceID;
 		String newDestID = ""+ theDestID;
 		
 		createChildren(cEdge, new String[]{"edgeID","edgeName","value","probability","selected","sourceNodeID","destinationNodeID"}, new String[]{newEdgeID,newEdgeName,newVal,newProb,newSelected,newSourceID,newDestID});

 		return cEdge;
    }

    /**
     * Creates a DecisionNodeElement, DNode ele,  with the given attributes and append it to curEle
     * @param curEle - the element in which the DNode ele appends to
     * @param theTreeNodeID - treeNode of the DecisionNode
     * @param theTime - time of the DecisionNode
     * @param theVarName - variableName of the DecisionNode
     * @param theSelected - selected of the DecisionNode
     * @return DNode Ele
     */
    public Element CreateDecisionNodeElement(Element curEle, int theTreeNodeID, double theTime, String theVarName, double theRollBackValue, boolean theSelected, int theSourceID, String nameOfBestDecisionOrNull, int idOfBestDecisionOrMinusOne)
    {

        Element DNode = createDNodeEle(theTreeNodeID, theTime, theVarName, theRollBackValue, theSelected, theSourceID, nameOfBestDecisionOrNull, idOfBestDecisionOrMinusOne);
        //append DNode to the current element
        curEle.appendChild(DNode);

		return DNode;
    }
    public Element createDNodeEle(int theTreeNodeID, double theTime, String theVarName, double theRollBackValue, boolean theSelected, int theSourceID, String nameOfBestDecisionOrNull, int idOfBestDecisionOrMinusOne){
    	 Element DNode = dom.createElement("TreeNode");
 		DNode.setAttribute("type", "DecisionNode");

 		//create treeNodeID element with its values and append to DNode Element
 		String newTreeNodeID = ""+theTreeNodeID;
 		String newTime = ""+theTime;
 		String newVarName = theVarName;
 		
 		createChildren(DNode,new String []{"treeNodeID","time","variableName"} ,new String []{newTreeNodeID,newTime,newVarName});

         if(theRollBackValue != Double.NaN)
         {
             //create terVal element with its values and append to DNode Element
         	String newRollBackValue = ""+theRollBackValue;
         	appendChild(DNode,"rollBackValue",newRollBackValue);
         	
         	if (nameOfBestDecisionOrNull != null)
         	{
         		appendChild(DNode,"nameOfBestDecision",nameOfBestDecisionOrNull);
         		appendChild(DNode,"idOfBestDecision",""+idOfBestDecisionOrMinusOne);
         	}
         }

         //create selected element with its values and append to DNode Element
         String newSelected = ""+theSelected;
 //        DNodeappendChild(DNode,"selected",newSelected);
         createChildren(DNode,new String []{"selected"} ,new String []{newSelected});
         // if sourceID == -1 then it must be a root node so omit the sourceID
         if(theSourceID != -1)
         {
             //create sourceID element with its values and append to DNode Element
         	String newSourceID = ""+ theSourceID;
         	appendChild(DNode,"sourceEdgeID",newSourceID);
            
         }
         return DNode;
    }

    /**
     * Creates a ChanceNodeElement, CNode ele,  with the given attributes and append it to curEle
     * @param curEle - the element in which the CNode ele appends to
     * @param theTreeNodeID - treeNode of the ChanceNode
     * @param theTime - time of the ChanceNode
     * @param theVarName - variableName of the ChanceNode
     * @param theSelected - selected of the ChanceNode
     * @return CNode Ele
     */
    public Element CreateChanceNodeElement(Element curEle, int theTreeNodeID, double theTime, String theVarName, double theRollBackValue, boolean theSelected, int theSourceID)
    {
    	Element CNode = dom.createElement("TreeNode");
		CNode.setAttribute("type", "ChanceNode");

		//create treeNodeID element with its values and append to DNode Element
		String newTreeNodeID = ""+theTreeNodeID;
		String newTime = ""+theTime;
		String newVarName = theVarName;
		
		createChildren(CNode,new String []{"treeNodeID","time","variableName"} ,new String []{newTreeNodeID,newTime,newVarName});

        if(theRollBackValue != Double.NaN)
        {
            //create terVal element with its values and append to DNode Element
        	 String newRollBackValue = ""+theRollBackValue;
        	appendChild(CNode,"rollBackValue",newRollBackValue);     
        }

        //create selected element with its values and append to DNode Element
        String newSelected = ""+theSelected;
        appendChild(CNode,"selected",newSelected);  
		

        // if sourceID == -1 then it must be a root node so omit the sourceID
        if(theSourceID != -1)
        {
            //create sourceID element with its values and append to DNode Element
        	String newSourceID = ""+ theSourceID;
        	appendChild(CNode,"sourceEdgeID",newSourceID);  
            
        }       
        //append DNode to the current element
        curEle.appendChild(CNode);

		return CNode;
    }

    /**
     * Creates a TerminalNodeElement, TNode ele,  with the given attributes and append it to curEle
     * @param curEle - the element in which the CNode ele appends to
     * @param theTreeNodeID - treeNode of the ChanceNode
     * @param theTime - time of the ChanceNode
     * @param theTerVal - terminalValuue of the ChanceNode
     * @param theSelected - selected of the ChanceNode
     * @return TNode Ele
     */
    public Element CreateTerminalNodeElement(Element curEle, int theTreeNodeID, double theTime, String theVarName, double theTerVal, boolean theSelected, int theSourceID)
    {
    	Element TNode = dom.createElement("TreeNode");
		TNode.setAttribute("type", "TerminalNode");

		//create treeNodeID element with its values and append to DNode Element
		String newTreeNodeID = ""+theTreeNodeID;
		String newTime = ""+theTime;
		String newVarName = theVarName;
		
		createChildren(TNode,new String []{"treeNodeID","time","variableName"} ,new String []{newTreeNodeID,newTime,newVarName});;

        // if there is not terminal value don't output
        if(theTerVal != Double.NaN)
        {
            //create terVal element with its values and append to DNode Element
        	String newTerVal = ""+theTerVal;
        	appendChild(TNode,"terminalValue",newTerVal);         
        }

        //create selected element with its values and append to DNode Element
        String newSelected = ""+theSelected;
        appendChild(TNode,"selected",newSelected);

        // if sourceID == -1 then it must be a root node so omit the sourceID
        if(theSourceID != -1)
        {
            //create sourceID element with its values and append to DNode Element
        	String newSourceID = ""+ theSourceID;
        	appendChild(TNode,"sourceEdgeID",newSourceID);
        }

        //append DNode to the current element
        curEle.appendChild(TNode);

		return TNode;
    }
    public void createChildren(Element e, String [] arrayChildrenNames, String []arrayChildrenValues)
	{
	    for (int i = 0; i < arrayChildrenNames.length; i++){
	 		appendChild(e,arrayChildrenNames[i],arrayChildrenValues[i]);
	    }
	    
	}
    public void appendChild(Element e,String ElementName,String TextNodeName){
    	Element Ele = dom.createElement(ElementName);
		Text edgeInfo = dom.createTextNode(TextNodeName);
		Ele.appendChild(edgeInfo);
		e.appendChild(Ele);
    }
    
    
    /**
     * Create the element createChildrenEdgeList
     * @param curEle the element in which ne childrenEdgeList element appends to
     * @return the childrenEdgeList element
     */
    public Element createChildrenEdgeList(Element curEle)
    {
        Element CEList = dom.createElement("childrenEdgeList");

        //append childrenEdgeList to the current element
        curEle.appendChild(CEList);

        return CEList;
    }

    /**
     * Create the element createChildList
     * @param curEle the element in which ne ChildList element appends to
     * @return the ChildList element
     */
    public Element createChildList(Element curEle)
    {
        Element CList = dom.createElement("childList");

        //append DNode to the current element
        curEle.appendChild(CList);

        return CList;
    }


	/**
	 * This method uses Xerces specific classes
	 * prints the XML document to file.
     */
	public void printToFile(String outfile){

		try
		{
			//print
			OutputFormat format = new OutputFormat(dom);
			format.setIndenting(true);

			//to generate output to console use this serializer
			//XMLSerializer serializer = new XMLSerializer(System.out, format);


			//to generate a file output use fileoutputstream instead of system.out
			XMLSerializer serializer = new XMLSerializer(
			new FileOutputStream(new File(outfile)), format);

			serializer.serialize(dom);
			System.err.println("Output of XML file " + outfile + " is complete.");

		} catch(IOException ie) {
			System.err.println("Error encountered in output of XML file " + outfile + ".");
		    ie.printStackTrace();
		}
	}
}

